#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QTableWidget>
#include <QRandomGenerator>
#include <QVBoxLayout>
#include <QHeaderView>
#include <vector>
#include <algorithm>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow),currentStep(0)
{
    ui->setupUi(this);
    table = new QTableWidget(this);
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(table);
    // ui->centralwidget->setLayout(layout);
    // setLayout(layout);
    // ui->centralwidget_2->setLayout(layout);
    ui->widget2->setLayout(layout);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_generateButton_clicked()
{
     generatePageSequence();
}

void MainWindow::generatePageSequence()
{
    pageSequence.clear();
    for (int i = 0; i < 20; ++i) {
        pageSequence.push_back(QRandomGenerator::global()->bounded(10));
    }
    ui->pageSequenceEdit->setText(QString::fromStdString(
        std::accumulate(pageSequence.begin(), pageSequence.end(), std::string(),
                        [](const std::string &a, int b) -> std::string { return a + (a.length() > 0 ? "," : "") + std::to_string(b); })));
}


void MainWindow::on_runFIFOButton_clicked()
{
    runFIFO();
}

void MainWindow::runFIFO()
{
    int framesCount = ui->framesSpinBox->value();
    if(framesCount == 0)
        framesCount = 1;
    std::vector<int> frames(framesCount, -1);
    std::vector<std::vector<int>> result(pageSequence.size(), std::vector<int>(framesCount, -1));
    int pageFaults = 0;
    int nextFrameToReplace = 0;
    std::vector<int>  evictedpages(pageSequence.size(),-1);
    for (size_t i = 0; i < pageSequence.size(); ++i) {
        int page = pageSequence[i];
        auto it = std::find(frames.begin(), frames.end(), page);
        if (it == frames.end()) {
            if (frames[nextFrameToReplace] != -1) {
                evictedpages[i] = frames[nextFrameToReplace];
            }
            frames[nextFrameToReplace] = page;
            nextFrameToReplace = (nextFrameToReplace + 1) % framesCount;
            pageFaults++;
        }
        result[i] = frames;
    }
    displayResults("FIFO", result, evictedpages, pageFaults);
}
void MainWindow::on_runOPTButton_clicked()
{
    runOPT();
}

void MainWindow::runOPT()
{
    int framesCount = ui->framesSpinBox->value();
    if(framesCount == 0)
        framesCount = 1;
    std::vector<int> frames(framesCount,-1);
    std::vector<std::vector<int>> result(pageSequence.size(), std::vector<int>(framesCount, -1));
    int pageFaults = 0;
    std::vector<int>  evictedPages(pageSequence.size(),-1);
    for (size_t i = 0; i < pageSequence.size(); ++i) {
        int page = pageSequence[i];
        auto it = std::find(frames.begin(), frames.end(), page);
        if (it == frames.end()) { // Page fault
            int index = -1;
            if (std::find(frames.begin(), frames.end(), -1) != frames.end()) {
                index = std::find(frames.begin(), frames.end(), -1) - frames.begin();
            } else {
                int farthest = -1;
                for (int j = 0; j < framesCount; ++j) {
                    int nextID= std::find(pageSequence.begin() + i + 1, pageSequence.end(), frames[j]) - pageSequence.begin();
                    if (nextID > farthest) {
                        farthest = nextID;
                        index = j;
                    }
                }
            }
            if (frames[index] != -1) {
                evictedPages[i] = frames[index];
            }
            frames[index] = page;
            pageFaults++;
        }
        result[i] = frames;
    }
    displayResults("OPT", result, evictedPages, pageFaults);
}

void MainWindow::on_runLRUButton_clicked()
{
    runLRU();
}

void MainWindow::runLRU()
{
    int framesCount = ui->framesSpinBox->value();
    std::vector<int> frames(framesCount, -1);
    std::unordered_map<int, std::list<int>::iterator> pageMap;
    std::list<int> lruList;
    std::vector<std::vector<int>> result(pageSequence.size(), std::vector<int>(framesCount, -1));
    std::vector<int> evictedPages(pageSequence.size(), -1);
    int pageFaults = 0;

    for (size_t i = 0; i < pageSequence.size(); ++i) {
        int page = pageSequence[i];
        auto it = std::find(frames.begin(), frames.end(), page);

        if (it == frames.end()) { // Page fault
            if (std::find(frames.begin(), frames.end(), -1) != frames.end()) {
                int replaceIndex = std::find(frames.begin(), frames.end(), -1) - frames.begin();
                frames[replaceIndex] = page;
                pageMap[page] = lruList.insert(lruList.end(), page);
            } else {
                int lruPage = lruList.front();
                lruList.pop_front();
                int replaceIndex = std::find(frames.begin(), frames.end(), lruPage) - frames.begin();
                evictedPages[i] = lruPage;
                frames[replaceIndex] = page;
                pageMap.erase(lruPage);
                pageMap[page] = lruList.insert(lruList.end(), page);
            }
            pageFaults++;
        } else {
            lruList.erase(pageMap[page]);
            pageMap[page] = lruList.insert(lruList.end(), page);
        }

        result[i] = frames;
    }

    displayResults("LRU", result, evictedPages, pageFaults);
}

void MainWindow::displayResults(const QString &algorithm, const std::vector<std::vector<int>> &results,const std::vector<int> & evictedPages,int pageFaults)
{
    table->clear();
    int rowCount = results[0].size() + 1;
    int columnCount = results.size();
    table->setRowCount(rowCount);
    table->setColumnCount(columnCount);
    table->setFixedSize(700,300);
    QStringList headers;
    for (int i = 0; i < columnCount; ++i) {
        headers << QString::number(i + 1);
    }
    table->setHorizontalHeaderLabels(headers);

    for (int col = 0; col < columnCount; ++col) {
        for (int row = 0; row < rowCount - 1; ++row) {
            int page = results[col][row];
            QTableWidgetItem *item = new QTableWidgetItem(page == -1 ? "" : QString::number(page));
            setColor(item, page);
            table->setItem(row, col, item);
        }

        int evictedPage = evictedPages[col];
        QTableWidgetItem *evictedItem = new QTableWidgetItem(evictedPage == -1 ? "" : QString::number(evictedPage));
        table->setItem(rowCount - 1, col, evictedItem);
    }

    double faultRate = static_cast<double>(pageFaults) / pageSequence.size();
    ui->resultLine->setText(QString("Total Page Faults: %1, Fault Rate: %2%").arg(pageFaults).arg(faultRate * 100, 0, 'f', 2));

}


void MainWindow::setColor(QTableWidgetItem *item, int page)
{
    if (page != -1) {
        int colorValue = (page * 255 / 9) % 255;

        item->setBackground(QColor::fromRgb(colorValue, 255 - colorValue, colorValue / 2));
    }
}
